#ifndef __PACKMAN_H
#define __PACKMAN_H

#include "../liolib/lio3d.h"
#include "../liolib/liomusic.h"

#define DATA_DIR _T("./data/")
#define WIN_W 1024
#define WIN_H 768
#define BPP 32
#define	MONST_COUNT 4
#define IMG_W 64
#define IMG_H 64
#define IMG_PIXELS 4096
#define BACK_COLOR 0xff333333
#define TRANSP_COLOR 0xffc00000
#define CHANGE_COLOR 0xff00c0c0
#define COLOR_TOLER 0.38f
#define STRONG_TIME 14500
#define CHERRY_TIME 10000
#define CHERRY_PERIOD 30000
#define LEV_W 13
#define LEV_H 12
#define MIDI_TEMPO 120
#ifdef _DEBUG
#define TITLE_PACMAN _T("PacManLio 1.2 DEBUG")
#define SPEED_START 2
#else
#define TITLE_PACMAN _T("PacManLio 1.2")
#define SPEED_START 1
#endif
#define SPEED_MAX 5
#define SPEED_MIN 1
#define LEV_LEFT 0
#define LEV_BOT 0
#define TEXT_X 905
#define TEXT_Y 750
#define FONT_SIZE 16
#define IMG_FILE_ID 300
#define MAX_MS 100
#define PIX_TO_LEV_X(n) (WORD)(((n)-LEV_LEFT)/IMG_W)
#define PIX_TO_LEV_Y(n) (WORD)(((n)-LEV_BOT)/IMG_H)
#define LEV_TO_PIX_X(n) ((n+0.5)*IMG_W+LEV_LEFT)
#define LEV_TO_PIX_Y(n) ((n+0.5)*IMG_H+LEV_BOT)
#define SUSPENSE_TIME 3000
#define ENTRY_TOLER 10
#define COLLIS_TOLER 30
#define INIT_FILE_NAME _T("pacman.ini")
#define SCORE_FILE_NAME _T("score.dat")
#define LIFE_COUNT 3
#define LIFE_SCORE 100
#define LIFE_X 920
#define LIFE_Y 10
#define MAX_MS 100
#define SPEED_MODIF_INC 0.01
#define MAX_LIVES 5
#define MSG_LEVEL_ERROR _T("Level error!")

namespace Lio {

typedef Vector<Image> ImgVector;
typedef Vector<WORD[LEV_H][LEV_W]> LevelVector;
typedef Point2d<double> Position;
typedef Point2d<WORD> Location;
typedef struct _MonsterInfo
{
	DWORD color;
	double speed;
	WORD wait;
} MonsterInfo;

enum OrientID{
	ORIENT_RIGHT,
	ORIENT_LEFT,
	ORIENT_COUNT
};
enum MediaID{
	MEDIA_IMG,
	MEDIA_SND,
	MEDIA_MUS,
	MEDIA_COUNT
};
enum SndID{
	SND_STOP,
	SND_POINT,
	SND_BIG_POINT,
	SND_MONST_DEAD,
	SND_PACKMAN_DEAD,
	SND_NEW_LIFE,
	SND_VICTORY,
	SND_CHERRY,
	SND_COUNT
};
enum ImgID{
	IMG_NONE,
	IMG_WALL,
	IMG_POINT,
	IMG_BIG_POINT,
	IMG_CHERRY,
	IMG_COUNT
};
enum MapID{
	MAP_PACMAN,
	MAP_HOME,
	MAP_COUNT
};
enum ObjectID
{
	OBJ_NONE,
	OBJ_WALL,
	OBJ_POINT,
	OBJ_BIG_POINT,
	OBJ_HERO_HOME,
	OBJ_MONST_HOME_1,
	OBJ_MONST_HOME_2,
	OBJ_MONST_HOME_3,
	OBJ_MONST_HOME_4,
	OBJ_MONST_HOME,
	OBJ_CHERRY,
	OBJ_COUNT
};
enum WaitID
{
	SUSPENSE_NONE,
	SUSPENSE_DEAD,
	SUSPENSE_LEVEL,
	SUSPENSE_COUNT
};
enum MusID{
	MUS_WEAK,
	MUS_STRONG,
	MUS_COUNT
};
enum StateID{
	STATE_WEAK,
	STATE_STRONG,
	STATE_DEAD,
	STATE_COUNT
};
enum CreatureID{
	CREATURE_MONST,
	CREATURE_PACKMAN,
	CREATURE_COUNT
};
CSTR strAbrev[MEDIA_COUNT]={
	_T("img"),
	_T("snd"),
	_T("mus")
};
CSTR strExt[MEDIA_COUNT]={
	_T("bmp"),
	_T("wav"),
	_T("mid")
};

enum ParamID{
	PARAM_ANIM, // count of sprites for anim
	PARAM_TIME,	// time between sprites for anim
	PARAM_SPEED,// creature movement speed
	PARAM_WAVE_A, // oscillation amplitude
	PARAM_WAVE_W, // oscillation frequancy
	PARAM_COUNT
};
enum BonusID
{
	BONUS_POINT,
	BONUS_BIG_POINT,
	BONUS_MONSTER,
	BONUS_CHERRY,
	BONUS_COUNT
};

void drawImg(const Image * img, double x, double y);
BYTE getImgID(int id, int state, int anim){return 100*id+10*state+anim;}
BOOL flipImageH(const Image *imgSrc, Image *imgDst);

class Pacman;
class Creature
{
	const Pacman * parent;
	BYTE id;
public:
	BYTE orient,state;
	char dirx,diry;
	DWORD timer,time;
	double wavePhi,speed,waveW,kSpeed;
	WORD animTime,animCount,anim,waveA,susTime;
	ImgVector imgs[STATE_COUNT][ORIENT_COUNT];
	Location home,loc;
	Position pos,wave,target;
	BOOL animInc;

	Creature():	state(0),id(0),speed(0),susTime(0),kSpeed(1),
				waveA(0),waveW(0),orient(ORIENT_RIGHT),dirx(0),diry(0),
				anim(0),animCount(0),animTime(0),animInc(TRUE),wavePhi(0){}
	BOOL move(DWORD ms);
	void init(const Pacman * parent, BYTE id, BYTE state, double speedCoef, WORD waitStartTime);
	void setState(BYTE state);
	void draw()const{drawImg(getImg(),(UINT)(pos.x+wave.x-0.5*IMG_W),(UINT)(pos.y+wave.y-0.5*IMG_H));}
	void setDir(char newDirX, char newDirY);
	void setHome(WORD w, WORD h)
	{
		home.x=loc.x=w;
		home.y=loc.y=h;
		target.x=pos.x=LEV_TO_PIX_X(w);
		target.y=pos.y=LEV_TO_PIX_Y(h);
	}
	Image *getImg()const{return imgs[state][orient].elems+anim;}
};

class Pacman:public Application
{
public:
	static const WORD bonuses[BONUS_COUNT];
	static const double susTimes[SUSPENSE_COUNT];
	static const WORD params[CREATURE_COUNT][STATE_COUNT][PARAM_COUNT];
	static const MonsterInfo monstInfo[MONST_COUNT];

	double levPixW[LEV_W],levPixH[LEV_H];
	WORD maps[MAP_COUNT][LEV_H][LEV_W],level[LEV_H][LEV_W];
	LevelVector levels;
	BOOL check(DWORD w, DWORD h)const{return (w>=0&&w<LEV_W&&h>=0&&h<LEV_H&&level[h][w]!=OBJ_WALL);}
protected:
	BYTE suspense,monstCount,lives;
	WORD levPoints,kSpeed,score;
	DWORD loops,mapop;
	int levCount,levIndex;
	long strongTime,highScore,cherryTime;
	double runTime,susTime;
	Font font;
	BOOL running;
	Image imgs[IMG_COUNT];
	_TCHAR strbuf[MAX_PATH];
	Image buff;
	MidiPlayer midi;
	Creature pacman,monst[MONST_COUNT];

	BOOL create();
	BOOL glCreate();
	void glDestroy();
	void destroy();
	void reshape();
	BOOL input(BYTE type);
	void update();
	void draw();
	BOOL callback();
	BOOL buildImg(	Image *newImage,
					int transpColor = TRANSP_COLOR,
					int changeColor = CHANGE_COLOR,
					int newColor = 0);
	CSTR getFileName(int id, int media)
	{
		_stprintf(strbuf,_T("%s%s_%03d.%s"),DATA_DIR,strAbrev[media],id,strExt[media]);
		return strbuf;
	}
	BOOL loadImg(int id)
	{
		return readBitmap(getFileName(id,MEDIA_IMG),&buff);
	}
	BOOL playSnd(int id)
	{
		return PlaySound(getFileName(id,MEDIA_SND),NULL,SND_ASYNC|SND_FILENAME|SND_NODEFAULT);
	}
	void incScore(int bonusId)
	{
		WORD oldScore=score;
		score+=bonuses[bonusId];
		if(highScore<score)
			highScore=score;
		if(oldScore%LIFE_SCORE>score%LIFE_SCORE&&lives<MAX_LIVES)
		{
			lives++;
			playSnd(SND_NEW_LIFE);
		}
	}
	void setState(BYTE state);
	void growPotMap(DWORD w, DWORD h, BYTE id, WORD pot);
	void buildPotMap(DWORD w, DWORD h, BYTE id)
	{
		ClearMemory(maps[id],sizeof(maps[id]));
		mapop=0;
		growPotMap(w,h,id,1);
	}
	BOOL initLevel();
	long getCherryPeriod(){return (long)(-CHERRY_PERIOD*random(0.5,2.0));}
	void reset();
	DWORD getTimeMS(double sec)const{return (DWORD)min((1e3*kSpeed*sec+0.5),MAX_MS);}
	BOOL setDir(Creature &crt, BYTE id, BOOL toMin);
	ThreadInfo tiSound;
};

};

#endif